package dk.jwood.android.graph;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.joda.time.DateTime;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.util.Log;

public class DataProviderUtils {

	public static class SinIterator implements Iterator<Float> {

		private double counter;
		
		private final float increments;
		
		private final float offset;
		private final float factor;
		
		public SinIterator(float mean, float factor, float increments) {
			this.offset = mean;
			this.factor = factor;
			
			this.increments = increments;
			
			this.counter = 0d;
		}
		
		@Override
		public boolean hasNext() {
			return true;
		}

		@Override
		public Float next() {
			Double result = offset + Math.sin(counter) * factor;
			
			counter += increments;
			
			return result.floatValue();
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
		
		
	}

	public interface DataPointsGenerator {
		
		public void startGenerator();
		
		public void stopGenerator();
		
		
	}
	
	public static class RandomDataPointProvider implements DataPointsGenerator {

		protected static final String TAG = "RandomDataPointProvider";

		private final DataProvider dataProvider;

//		private final Random random;
//		
		
		private final int msPeriod;
		
		private final List<Iterator<Float>> values;

		private final AtomicReference<ScheduledFuture<?>> handlerRef;

		public RandomDataPointProvider(DataProvider dataProvider, Iterator<Float> values, int periodInMillis) {
			this(dataProvider, Collections.singletonList(values), periodInMillis);	
		}
		
		public RandomDataPointProvider(DataProvider dataProvider, List<Iterator<Float>> values, int periodInMillis) {
			this.dataProvider = dataProvider;

			this.values = values;
			
			this.msPeriod = periodInMillis;
			
			handlerRef = new AtomicReference<ScheduledFuture<?>>();

			Log.d(TAG, "create Randome generator");


		}

		private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);


		public void startGenerator() {

			final Runnable beeper = new Runnable() {
				@Override
				public void run() {


					long timestamp = new DateTime().getMillis();
					//					double value = random.nextDouble() * (maxValue - minValue);

					float[] vals = new float[dataProvider.getValuesCount()];
					for(int i = 0; i < vals.length; i++) {
						vals[i] = values.get(i).next();
					}


					Log.d(TAG, "createPoint: "+timestamp+" = "+vals);

					dataProvider.addDataPoint(timestamp, vals);

				}
			};

			Log.d(TAG, "Starting generator...");

			final ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 1, msPeriod, TimeUnit.MILLISECONDS);


			handlerRef.set(beeperHandle);
//			scheduler.schedule(new Runnable() {
//				public void run() { 
//					Log.d(TAG, "Stopping generator...");
//					beeperHandle.cancel(true); 
//				}
//			}, 60 * 60, TimeUnit.SECONDS);

		}

		public void stopGenerator() {

			if(handlerRef.get() != null) {
				handlerRef.get().cancel(true);
			}

		}
	}
	
	
	
	public static class SensorProvider implements SensorEventListener, DataPointsGenerator {

		private static final String TAG = "SensorProvider";

		private final DataProvider dataProvider;
		
		private final SensorManager sm;
		private final Sensor sensor;
		
		public SensorProvider(DataProvider dataProvider, SensorManager sm, Sensor sensor) {
			
			this.dataProvider = dataProvider;
			
			this.sm = sm;
			
			this.sensor = sensor;
			
		}
		
		public SensorProvider(DataProvider dataProvider, SensorManager sm, int sensorType) {
			this(dataProvider, sm, sm.getDefaultSensor(sensorType));
		}
		
		
		@Override
		public void onSensorChanged(SensorEvent event) {
			
//			Log.d(TAG, "onSensorChange: ts="+event.timestamp+" values="+event.values);
//			long millisTimestamp = event.timestamp / 1000;
			
			if(event == null) Log.d(TAG, "event was NULL!!!");
			
			if(event.values == null) Log.d(TAG, "no values in event "+event);
			
			long timestamp = new DateTime().getMillis();
			
			float[] cloned = Arrays.copyOf(event.values, event.values.length); 
			
			if(cloned == null) Log.d(TAG, "copy was null!!! "+Arrays.asList(event.values));
			
			dataProvider.addDataPoint(timestamp, cloned);
		}

		@Override
		public void onAccuracyChanged(Sensor sensor, int accuracy) {
			
		}


		@Override
		public void startGenerator() {
			
			Log.d(TAG, "register listener for sensor: "+sensor);
			
			sm.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
		}


		@Override
		public void stopGenerator() {
			sm.unregisterListener(this);
		}
		
		
		
		
	}


	


}
